/**
 * 
 */
package com.qolome.utils.security;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.qolome.enums.ResultEnum;
import com.qolome.exception.QolomeException;
import com.qolome.utils.HashUtil;
import com.qolome.utils.web.HttpResult;

import lombok.extern.slf4j.Slf4j;

/**
 * @Description: AESUtils aes对称加密解密工具类, 注意密钥不能随机生机, 不同客户端调用可能需要考虑不同Provider,
 *               考虑安卓与IOS不同平台复杂度,简化不使用Provider
 * @author 姜锋
 * @date 2020年1月3日 下午10:24:12
 * @version V1.0
 */
@Slf4j
public class AESUtils {
	/**
	 * 使用PKCS7Padding填充必须添加一个支持PKCS7Padding的Provider 类加载的时候就判断是否已经有支持256位的Provider,如果没有则添加进去
	 */
	static {
		if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
			Security.addProvider(new BouncyCastleProvider());
		}
	}

	
	/**
	 * 
	 * @Description: 加密 128位
	 * @author 姜锋
	 * @date 2020年1月4日 上午9:44:17
	 * @param @param content 需要加密的原内容
	 * @param @param pkey 密匙
	 * @param @return
	 * @param @throws QolomeException
	 * @return byte[]
	 * @throws
	 */
	public static byte[] aesEncrypt(String content, String pkey,byte[] vi) throws QolomeException {
		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
			cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(pkey.getBytes(), "AES"), new IvParameterSpec(vi));
			return cipher.doFinal(content.getBytes("UTF-8"));
		} catch (NoSuchAlgorithmException e) {
			// 算法
			log.error("aesEncrypt() 算法未找到异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesEncrypt() 算法未找到异常:" + e.getMessage()));
		} catch (NoSuchPaddingException e) {
			// 填充
			log.error("aesEncrypt() 填充方式未找到异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(),
					"aesEncrypt() 填充方式未找到异常():" + e.getMessage()));
		} catch (InvalidKeyException e) {
			// key初始化异常
			log.error("aesEncrypt() key初始化异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(),
					"aesEncrypt() key初始化异常:" + e.getMessage()));
		} catch (InvalidAlgorithmParameterException e) {
			// key初始化异常
			log.error("aesEncrypt() 无效算法参数异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(),
					"aesEncrypt() 无效算法参数异常:" + e.getMessage()));
		} catch (IllegalBlockSizeException e) {
			// key初始化异常
			log.error("aesEncrypt() 非法块大小异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesEncrypt() 非法块大小异常:" + e.getMessage()));
		} catch (BadPaddingException e) {
			log.error("aesEncrypt() 填充异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesEncrypt() 填充异常:" + e.getMessage()));
		} catch (UnsupportedEncodingException e) {
			log.error("aesEncrypt() 编码不支持 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesEncrypt() 编码不支持异常:" + e.getMessage()));
		}
	}
	
	/**
	 * 
	 * @Description: 获得密钥
	 * @author 姜锋
	 * @date 2020年1月4日 上午9:44:36
	 * @param @param secretKey
	 * @param @return
	 * @param @throws QolomeException
	 * @return SecretKey
	 * @throws
	 */
	protected static SecretKey generateKey(String secretKey) throws QolomeException {
		// 防止linux下 随机生成key
		SecureRandom secureRandom;
		try {
			secureRandom = SecureRandom.getInstance("SHA1PRNG", Security.getProvider("SUN"));
			secureRandom.setSeed(secretKey.getBytes());
			KeyGenerator kg;
			kg = KeyGenerator.getInstance("AES");
			kg.init(secureRandom);
			// 生成密钥
			return kg.generateKey();
		} catch (NoSuchAlgorithmException e) {
			// 算法
			log.error("generateKey() 算法未找到异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(),
					"generateKey() 算法未找到异常:" + e.getMessage()));
		}
	}
	
	/**
	 * 
	 * @Description: aes对称加密
	 * @author 姜锋
	 * @date 2020年1月4日 上午9:42:54
	 * @param @param content 加密前原内容
	 * @param @param pkey 长度为16个字符,128位
	 * @param @return base64EncodeStr aes加密完成后内容
	 * @return String base64EncodeStr aes加密完成后内容
	 */
	public static String aesEncryptStr(String content, String pkey,byte[] vi) {
		return Base64.encodeBase64String(aesEncrypt(content, pkey,vi));
	}
	
	/**
	 * 
	 * @Description:content base64处理过的字符串
	 * @author 姜锋
	 * @date 2020年1月4日 上午9:42:23
	 * @param @param content base64处理过的字符串
	 * @param @param pkey 密匙
	 * @param @return
	 * @param @throws QolomeException
	 * @return String 返回类型
	 * @throws
	 */
	public static String aesDecodeStr(String content, String pkey, byte[] vi) throws QolomeException {
		try {
			if (StringUtils.isBlank(content) || StringUtils.isBlank(pkey)) {
				throw new QolomeException(
						HttpResult.operate(ResultEnum.ERROR_BINDING_RESULT.getCode(), "aesDecodeStr() 参数不匹配异常"));
			}
			return new String(aesDecode(Base64.decodeBase64(content), pkey,vi), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			log.error("aesEncrypt() 编码不支持 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesEncrypt() 编码不支持异常:" + e.getMessage()));
		}
	}
	/**
	 * 解密 128位
	 * @param content 解密前的byte数组
	 * @param pkey 密匙
	 * @return result 解密后的byte数组
	 * @throws Exception
	 */
	public static byte[] aesDecode(byte[] content, String pkey,byte[] vi) throws QolomeException {
		// SecretKey secretKey = generateKey(pkey);
		// byte[] enCodeFormat = secretKey.getEncoded();
		try {
			SecretKeySpec skey = new SecretKeySpec(pkey.getBytes(), "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");// 创建密码器
			cipher.init(Cipher.DECRYPT_MODE, skey, new IvParameterSpec(vi));// 初始化解密器
			return cipher.doFinal(content);
		}catch (NoSuchAlgorithmException e) {
			// 算法
			log.error("aesDecode() 算法未找到异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesDecode() 算法未找到异常:" + e.getMessage()));
		} catch (NoSuchPaddingException e) {
			// 填充
			log.error("aesDecode() 填充方式未找到异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(),
					"aesDecode() 填充方式未找到异常():" + e.getMessage()));
		} catch (InvalidKeyException e) {
			// key初始化异常
			log.error("aesDecode() key初始化异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesDecode() key初始化异常:" + e.getMessage()));
		} catch (InvalidAlgorithmParameterException e) {
			// key初始化异常
			log.error("aesDecode() 无效算法参数异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesDecode() 无效算法参数异常:" + e.getMessage()));
		} catch (IllegalBlockSizeException e) {
			// key初始化异常
			log.error("aesDecode() 非法块大小异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesEncrypt() 非法块大小异常:" + e.getMessage()));
		} catch (BadPaddingException e) {
			log.error("aesDecode() 填充异常 :{}", e.getMessage());
			e.printStackTrace();
			throw new QolomeException(
					HttpResult.operate(ResultEnum.ERROR_EXCEPTION.getCode(), "aesEncrypt() 填充异常:" + e.getMessage()));
		}
	}
	
	public static void test() throws QolomeException {
		// 明文
		String content = "qq245635595";
		// 密匙
		String pkey = "1234567890123456";
		byte[]  vi = HashUtil.getMd5("1234567890123456") .substring(16).getBytes();
		System.out.println("待加密报文:" + content);
		System.out.println("密匙:" + pkey);
		String aesEncryptStr = aesEncryptStr(content, pkey,vi);
		System.out.println("加密报文:" + aesEncryptStr);
		String aesDecodeStr = aesDecodeStr(aesEncryptStr, pkey,vi);
		System.out.println("解密报文:" + aesDecodeStr);
		System.out.println("加解密前后内容是否相等:" + aesDecodeStr.equals(content));
	}
}
